package com.freeman.cement.system;

import com.freeman.cement.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

/**
 * 前端请求地址举例：
 * ws://localhost:8080/sysmanage/wsEvent/001
 *
 */
@ServerEndpoint("/wsEvent/{token}")
@Component
public class WebSocketServer {
    private static final Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
    
    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
    private static ConcurrentHashMap<String, WebSocketServer> webSocketSet = new ConcurrentHashMap<String, WebSocketServer>();
    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;
    //当前发消息的人员编号
    private String username = "";

    public static synchronized int getOnlineCount() {
        return webSocketSet.size();
    }

//    public static synchronized int getOnlineCount() {
//        return onlineCount;
//    }
//
//    public static synchronized void addOnlineCount() {
//        WebSocketServer.onlineCount++;
//    }
//
//    public static synchronized void subOnlineCount() {
//        WebSocketServer.onlineCount--;
//    }

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(@PathParam(value = "token") String token, Session session, EndpointConfig config) {
        logger.info("收到WebSocket连接请求: token={}, sessionId={}, uri={}", token, session.getId(), session.getRequestURI());
        try {
            String username = null;
            // 1. 先尝试用token查找（兼容老逻辑）
            for (Map.Entry<String, User> entry : BllFrame.loginUsrsCache.entrySet()) {
                if (token.equals(entry.getValue().getToken())) {
                    username = entry.getKey();
                    break;
                }
            }
            // 2. 如果token查不到，尝试用加密user_code解密后查找
            if (username == null) {
                try {
                    String userCode = com.freeman.cement.util.AESUtil.decryptUserCode(token);
                    // 用userCode查找用户
                    for (Map.Entry<String, User> entry : BllFrame.loginUsrsCache.entrySet()) {
                        if (userCode.equals(entry.getValue().getUserCode())) {
                            username = entry.getKey();
                            break;
                        }
                    }
                    if (username != null) {
                        logger.info("通过加密user_code识别到用户: userCode={}, username={}", userCode, username);
                    }
                } catch (Exception e) {
                    logger.warn("user_code解密失败，token={}，错误:{}", token, e.getMessage());
                }
            }
            if (username == null) {
                logger.error("未找到匹配token或user_code的用户，拒绝WebSocket连接");
                try {
                    session.close();
                } catch (IOException e) {
                    logger.error("关闭WebSocket连接失败: {}", e.getMessage(), e);
                }
                return;
            }
            this.username = username;
            this.session = session;
            webSocketSet.put(session.getId(), this);
            logger.info("WebSocket连接建立成功: username={}, sessionId={}, 当前连接数={}, 连接列表={}", 
                username, session.getId(), getOnlineCount(), webSocketSet.keySet());
        } catch (Exception e) {
            logger.error("WebSocket连接建立失败: token={}, error={}", token, e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        if (!username.equals("")) {
            webSocketSet.remove(session.getId());
            logger.info("用户[{}]关闭WebSocket连接，当前WebSocket连接数: {}, 剩余连接列表: {}", 
                username, getOnlineCount(), webSocketSet.keySet());
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("收到来自用户[{}]的消息: {}", username, message);
        //session.get
        //群发消息
        if (1 < 2) {
            sendAll(message);
        } else {
            //给指定的人发消息
            sendToUser(message);
        }
    }

    /**
     * 给指定的人发送消息
     * @param message
     */
    private void sendToUser(String message) {
        String sendUserno = message.split("[|]")[1];
        String sendMessage = message.split("[|]")[0];
        String now = getNowTime();
        try {
            if (webSocketSet.get(sendUserno) != null) {
                logger.info("向用户[{}]推送消息: {}", sendUserno, message);
                webSocketSet.get(sendUserno).sendMessage(now + "用户" + username + "发来消息：" + " <br/> " + sendMessage);
            } else {
                logger.warn("用户[{}]不在线，无法推送消息", sendUserno);
            }
        } catch (IOException e) {
            logger.error("向用户[{}]推送消息失败: {}", sendUserno, e.getMessage(), e);
        }
    }

    /**
     * 给指定的人发送消息
     * @param message
     */
    private void sendToUser(String sendUserno, String message) {
        //String sendUserno = message.split("[|]")[1];
        //String sendMessage = message.split("[|]")[0];
        String now = getNowTime();
        try {
            if (webSocketSet.get(sendUserno) != null) {
                //webSocketSet.get(sendUserno).sendMessage(now + "用户" + username + "发来消息：" + " <br/> " + message);
                logger.info("向用户[{}]推送消息: {}", sendUserno, message);
                webSocketSet.get(sendUserno).sendMessage(message);
            } else {
                logger.warn("用户[{}]不在线，无法推送消息", sendUserno);
            }
        } catch (IOException e) {
            logger.error("向用户[{}]推送消息失败: {}", sendUserno, e.getMessage(), e);
        }
    }

    /**
     * 给所有人发消息
     * @param message
     */
    private void sendAll(String message) {
        String now = getNowTime();
        String sendMessage = message.split("[|]")[0];
        //遍历HashMap
        for (String key : webSocketSet.keySet()) {
            try {
                //判断接收用户是否是当前发消息的用户
                if (!username.equals(key)) {
                    logger.info("向用户[{}]推送消息: {}", key, message);
                    webSocketSet.get(key).sendMessage(now + "用户" + username + "发来消息：" + " <br/> " + sendMessage);
                    System.out.println("key = " + key);
                }
            } catch (IOException e) {
                logger.error("向用户[{}]推送消息失败: {}", key, e.getMessage(), e);
            }
        }
    }

    /**
     * 获取当前时间
     *
     * @return
     */
    private String getNowTime() {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = format.format(date);
        return time;
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("WebSocket发生错误: {}", error.getMessage(), error);
        if (session != null) {
            logger.error("错误会话信息: id={}, uri={}", session.getId(), session.getRequestURI());
        }
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解，是根据自己需要添加的方法。
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        logger.debug("消息已发送: {}", message);
        //this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 消息推送
     * @param username 用户名
     * @param message 消息内容
     * @return 是否推送成功
     */
    public static Boolean PushToUser(String username, String message) {
        logger.info("准备向用户[{}]推送消息，当前在线用户列表: {}", username, webSocketSet.keySet());
        
        // 遍历所有WebSocket连接，找到匹配的用户
        for (WebSocketServer ws : webSocketSet.values()) {
            if (username.equals(ws.username)) {
                try {
                    ws.sendToUser(ws.session.getId(), message);
                    logger.info("成功向用户[{}]推送消息", username);
                    return true;
                } catch (Exception e) {
                    logger.error("向用户[{}]推送消息失败: {}", username, e.getMessage(), e);
                }
            }
        }
        
        logger.warn("用户[{}]不在线，无法推送消息，当前在线用户列表: {}", username, webSocketSet.keySet());
        return false;
    }

}
